#pragma once
#include "nocopy.hpp"
#include "log.hpp"
#include <cerrno>
#include <string>
#include <sys/epoll.h>

class Epoller : public nocopy
{
    static const int size = 128;

public:
    Epoller()
    {
        // 创建一个epoll模型
        _epfd = epoll_create(size); // 传入的size参数随便写，但是要>0
        if (_epfd == -1)
        {
            lg(Error, "epoll_create: %s", strerror(errno));
        }
        else
        {
            lg(Info, "epoll_create success: %d", _epfd);
        }
    }

    // revents是输入输出型参数，获取就绪的事件
    int EpollerWait(struct epoll_event revents[], int num)
    {
        int n = epoll_wait(_epfd, revents, num, /*_timeout*/-1); // 返回值表示有几个文件描述符就绪
        return n;
    }

    // 对哪个套接字的什么事件执行什么动作（增删改）
    int EpollerUpdate(int oper, int sock, uint32_t event)
    {
        int n = 0;
        if (oper == EPOLL_CTL_DEL) // 如果想执行的是删除事件操作
        {
            n = epoll_ctl(_epfd, oper, sock, nullptr); // 对文件描述符的某个事件删除，设置为Null
            if (n != 0)                                // epoll_ctl成功返回0，不成功返回-1
            {
                lg(Error, "epoll_ctl delete error!");
            }
        }
        else // 增加、修改事件
        {
            struct epoll_event ev;
            ev.events = event; // 你要关心的事件设置进epoll_event结构体
            //把sock设置进去是方便后期得知是哪一个fd就绪，想得知监听套接字的读事件（连接）是否就绪
            ev.data.fd = sock; // 想对哪个文件描述符做操作

            n = epoll_ctl(_epfd, oper, sock, &ev); // 将栈上的变量更新进内核
            if (n != 0)                            // epoll_ctl成功返回0，不成功返回-1
            {
                lg(Error, "epoll_ctl error!");
            }
        }
        return n;
    }
    ~Epoller()
    {
        if (_epfd >= 0)
            close(_epfd);
    }

private:
    int _epfd;
    int _timeout{3000};
};